# -*- coding: utf-8 -*-
import numpy as np
import math
from collections import namedtuple

Point = namedtuple('key_point', ['x', 'y'])
Line = namedtuple('Line', ['begin', 'end'])


class Point_new(object):
    x = 0
    y = 0

    # 定义构造方法
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y


class Line_new(object):
    # a=0
    # b=0
    # c=0
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2


def GetLinePara(line):
    line.a = line.p1.y - line.p2.y;
    line.b = line.p2.x - line.p1.x;
    line.c = line.p1.x * line.p2.y - line.p2.x * line.p1.y;

def GetCrossPoint(l1, l2):
    GetLinePara(l1);
    GetLinePara(l2);
    d = l1.a * l2.b - l2.a * l1.b
    p = Point_new()
    p.x = (l1.b * l2.c - l2.b * l1.c) * 1.0 / d
    p.y = (l1.c * l2.a - l2.c * l1.a) * 1.0 / d
    return p

def get_cross_point(p1, p2, p3, p4):
    ## 计算由点p1,p2组成的直线和点p3,p4组成的直线的交点。
    p1 = Point_new(p1[0], p1[1])
    p2 = Point_new(p2[0], p2[1])
    p3 = Point_new(p3[0], p3[1])
    p4 = Point_new(p4[0], p4[1])

    line1 = Line_new(p1, p2)
    line2 = Line_new(p3, p4)
    pc = GetCrossPoint(line1, line2)
    return [pc.x, pc.y]
def vector_norm(data, axis=None, out=None):
    data = np.array(data, dtype=np.float64, copy=True)
    if out is None:
        if data.ndim == 1:
            return np.sqrt(np.dot(data, data))
        data *= data
        out = np.atleast_1d(np.sum(data, axis=axis))
        np.sqrt(out, out)
        return out
    else:
        data *= data
        np.sum(data, axis=axis, out=out)
        np.sqrt(out, out)
def angle_between_vectors(v0, v1, directed=True, axis=0):
    v0 = np.array(v0, dtype=np.float64, copy=False)
    v1 = np.array(v1, dtype=np.float64, copy=False)
    dot = np.sum(v0 * v1, axis=axis)
    dot /= vector_norm(v0, axis=axis) * vector_norm(v1, axis=axis)
    return np.arccos(dot if directed else np.fabs(dot))
def degree_four_points(v0_s, v0_e, v1_s, v1_e):
    """
    计算由四个点组成的两个向量间的夹角，用于计算侧弯角等。
    三个点的也可构造成四个点调用该函数。
    :param v0_s:第一个向量的起始点
    :param v0_e:第一个向量的终止点
    :param v1_s:第二个向量的起始点
    :param v1_e:第二个向量的终止点
    """
    v0 = np.asarray(v0_e) - np.asarray(v0_s)
    v1 = np.asarray(v1_e) - np.asarray(v1_s)
    ## 计算两条直线的交点，用来画角度标识的线。
    crosspoint = get_cross_point(v0_s, v0_e, v1_s, v1_e)
    value = np.rad2deg(angle_between_vectors(v0, v1))
    value = float(value)
    if value > 90:
        value = 180 - value
    return value,crosspoint
def min_angle_between_vector_and_vertical_line(vs, ve):
    """
        计算向量与竖直向量的最小夹角
        """
    v = np.asarray(ve) - np.asarray(vs)
    vt = np.asarray([vs[0], ve[1]]) - np.asarray(vs)
    drawer = [{'line': [vs, ve]},{'line':[vs, [vs[0], ve[1]]]}]
    value = np.rad2deg(angle_between_vectors(v, vt))
    value = float(value)
    return value, drawer


class HipMeasurement(object):
    """计算髋关节的相关指标。"""

    def __init__(self, data, spacing):
        self.data = data
        self.tags = ['RFH1', 'LOPAC', 'LIPSS', 'ROPAC', 'RIPTE',
                     'LFHCE', 'RFHCE', 'RIPSS', 'LIPTE', 'LFH1']

        self.points = {}
        self.spacing = spacing  ## 后面从csv里面读取
        for tag in self.tags:
            self.points[tag] = Point(x=self.get_value(tag)[0], y=self.get_value(tag)[1])
            # if self.points[tag] == Point(-100000, -100000):
            #     raise ValueError("Point(-10000, -10000): {}".format(tag))

    def get_value(self, tag):
        # print self.data[tag]
        return self.data[tag]

    def distance(self, point1, point2):
        """两点间欧式距离"""
        return math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2)

    def vertical_distance(self, point, begin, end):
        """点到直线的距离。
        point: 线外的点
        begin: 直线起始点
        end: 直线结束点
        """
        a = end.y - begin.y
        b = begin.x - end.x
        c = end.x * begin.y - begin.x * end.y

        dist = math.fabs(a * point.x + b * point.y + c) / math.pow(a * a + b * b, 0.5)

        return dist

    def foot_of_perpendicular(self, point, begin, end):
        """ 垂直点坐标。

        :param point: 线外的点
        :param begin: 直线起始点
        :param end: 直线结束点
        """
        dx = begin.x - end.x
        dy = begin.y - end.y
        u = (point.x - begin.x) * (begin.x - end.x) + (point.y - begin.y) * (begin.y - end.y)
        u = u / (dx * dx + dy * dy)
        foot_x = begin.x + u * dx
        foot_y = begin.y + u * dy
        return Point(x=foot_x, y=foot_y)

    def get_midpoint(self, point1, point2):
        """获得两点的中点坐标。"""
        return Point(x=(point1.x + point2.x) / 2., y=(point1.y + point2.y) / 2.)

    def cross_angle(self, line1, line2):
        """计算两个向量之间的夹角。"""
        vector1 = np.array([line1.end.x - line1.begin.x, line1.end.y - line1.begin.y])
        vector2 = np.array([line2.end.x - line2.begin.x, line2.end.y - line2.begin.y])

        cos_value = float(vector1.dot(vector2)) / (
                np.sqrt(vector1[0] ** 2 + vector1[1] ** 2) * np.sqrt(vector2[0] ** 2 + vector2[1] ** 2))
        # return np.arccos(cos_value) * (180 / np.pi)
        return np.arccos(cos_value) * (180 / np.pi)

    @property
    def lateral_ce_angle(self):
        """
        正常：25-40度， <=20 发育不良，>=40髋臼过深
        :return:
        """

        left_foot_of_perpendicular,left_foot_of_perpendicular_drawer = min_angle_between_vector_and_vertical_line(self.data['LFHCE'],self.data['LOPAC'])
        right_foot_of_perpendicular,right_foot_of_perpendicular_drawer = min_angle_between_vector_and_vertical_line(self.data['RFHCE'],self.data['ROPAC'])
        return left_foot_of_perpendicular,left_foot_of_perpendicular_drawer,right_foot_of_perpendicular,right_foot_of_perpendicular_drawer

    @property
    def tonnis_angle(self):
        """
        正常：0-10度， >10 发育不良， <0 不稳定性增加

        :return:
        """
        right_line1 = Line(begin=self.points['RIPSS'], end=self.points['ROPAC'])
        right_line2_shuiping = Line(begin=self.points['LIPTE'],
                                    end=self.points['RIPTE'])
        right_tonnis_angle = self.cross_angle(right_line1, right_line2_shuiping)

        left_line1 = Line(begin=self.points['LIPSS'], end=self.points['LOPAC'])
        left_line2_shuiping = Line(begin=self.points['RIPTE'],
                                   end=self.points['LIPTE'])
        left_tonnis_angle = self.cross_angle(left_line1, left_line2_shuiping)
        return round(left_tonnis_angle, 4), round(right_tonnis_angle, 4),\
               [self.points['LIPSS'].x + self.points['LIPTE'].x - self.points['RIPTE'].x,self.points['LIPSS'].y + self.points['LIPTE'].y - self.points['RIPTE'].y],\
               [self.points['RIPSS'].x + self.points['RIPTE'].x - self.points['LIPTE'].x,self.points['RIPSS'].y + self.points['RIPTE'].y - self.points['LIPTE'].y],
    @property
    def sharp_angle(self):
        """
        < 42度， 发育不良>40度
        :return:
        """
        right_line1 = Line(begin=self.points['RIPTE'], end=self.points['ROPAC'])
        right_line2 = Line(begin=self.points['LIPTE'], end=self.points['RIPTE'])
        right_sharp_angle = self.cross_angle(right_line1, right_line2)

        left_line1 = Line(begin=self.points['LIPTE'], end=self.points['LOPAC'])
        left_line2 = Line(begin=self.points['RIPTE'], end=self.points['LIPTE'])
        left_sharp_angle = self.cross_angle(left_line1, left_line2)

        return round(left_sharp_angle, 4), round(right_sharp_angle, 4)

    @property
    def femoral_head_extrusion_index(self):
        """
        < 0.25, 发育不良>=0.25
        :return:
        """
        # 半径
        right_radius = self.distance(self.points['RFH1'], self.points['RFHCE'])
        left_radius = self.distance(self.points['LFH1'], self.points['LFHCE'])
        ## 水平线，用两个泪滴构成
        h_line = Line(begin=self.points['RIPTE'], end=self.points['LIPTE'])

        ## 左OPAC水平垂线 垂点
        left_foot_of_perpendicular = self.foot_of_perpendicular(point=self.points['LOPAC'], begin=self.points['RIPTE'],
                                                                end=self.points['LIPTE'])
        ## 左OPAC水平垂线 垂点
        right_foot_of_perpendicular = self.foot_of_perpendicular(point=self.points['ROPAC'], begin=self.points['RIPTE'],
                                                                 end=self.points['LIPTE'])

        ## 圆心到线的距离
        left_vertical_distance = self.vertical_distance(point=self.points['LFHCE'], begin=self.points['LOPAC'],
                                                        end=left_foot_of_perpendicular)
        right_vertical_distance = self.vertical_distance(point=self.points['RFHCE'], begin=self.points['ROPAC'],
                                                         end=right_foot_of_perpendicular)

        ## 判断是圆心左边还是右边，注意x光片图像是左右相反的
        # 左关节
        if self.points['LFHCE'].x < self.points['LOPAC'].x:
            ## 正常的是这样的
            left_distance_b = left_radius - left_vertical_distance
        else:
            left_distance_b = left_radius + left_vertical_distance
        # 右关节
        if self.points['RFHCE'].x > self.points['ROPAC'].x:
            ## 正常的是这样的
            right_distance_b = right_radius - right_vertical_distance
        else:
            right_distance_b = right_radius + right_vertical_distance

        right_fhei = right_distance_b / (right_radius * 2)
        left_fhei = left_distance_b / (left_radius * 2)
        ## 返回计算结果以及左右两个垂点的坐标
        return round(left_fhei, 4), round(right_fhei, 4),[left_foot_of_perpendicular.x,left_foot_of_perpendicular.y],[right_foot_of_perpendicular.x,right_foot_of_perpendicular.y]

    @property
    def radius(self):
        right_radius = self.distance(self.points['RFH1'], self.points['RFHCE'])
        left_radius = self.distance(self.points['LFH1'], self.points['LFHCE'])
        return round(left_radius), round(right_radius)

    def get_result(self):
        # CE_angle
        left_foot_of_perpendicular, left_foot_of_perpendicular_drawer, right_foot_of_perpendicular, right_foot_of_perpendicular_drawer = self.lateral_ce_angle
        ## HHE_index
        left_femoral_head_extrusion_index,right_femoral_head_extrusion_index,left_femoral_head_extrusion_index_point,right_femoral_head_extrusion_index_point = self.femoral_head_extrusion_index
        ## sharp angle
        left_sharp_angle,right_sharp_angle = self.sharp_angle
        ## tonnis angle
        left_tonnis_angle,right_tonnis_angle,left_tonnis_angle_point,right_tonnis_angle_point = self.tonnis_angle

        value_dict = {
            'CE_angle': {'left':{
                'value': 90-left_foot_of_perpendicular, 'key_point':['LFHCE','LOPAC',],
                'drawer_point': left_foot_of_perpendicular_drawer
                },
                'right':{'value': 90-right_foot_of_perpendicular, 'key_point':['RFHCE','ROPAC'],
                    'drawer_point': right_foot_of_perpendicular_drawer}},

            'Tonnis_angle': {'left':{
                'value': left_tonnis_angle, 'key_point': ['LIPSS', 'LOPAC', 'LIPTE','RIPTE'],
                'drawer_point': [{'line':[self.data['LIPSS'], self.data['LOPAC']]}, {'line':[self.data['LIPSS'], left_tonnis_angle_point]}]},
                'right':{
                    'value': right_tonnis_angle, 'key_point': ['RIPSS', 'ROPAC', 'LIPTE','RIPTE'],
                    'drawer_point': [{'line':[self.data['RIPSS'], self.data['ROPAC']]}, {'line':[self.data['RIPSS'], right_tonnis_angle_point]}]}},
            'Sharp_angle': {'left':{
                'value': left_sharp_angle, 'key_point': ['RIPTE', 'LOPAC', 'LIPTE'],
                'drawer_point': [{'line': [self.data['LIPTE'], self.data['LOPAC']]}, {'line': [self.data['LIPTE'], self.data['RIPTE']]}]
                },
                'right':{'value': right_sharp_angle, 'key_point': ['RIPTE', 'ROPAC', 'LIPTE'],
                    'drawer_point': [{'line': [self.data['RIPTE'], self.data['ROPAC']]}, {'line': [self.data['LIPTE'], self.data['RIPTE']]}]}},
            'HHE_index': {'left':{
                'value': left_femoral_head_extrusion_index, 'key_point': ['LFH1', 'LFHCE','LIPTE','RIPTE','LOPAC','LFH3'],
                'drawer_point': [
                           {'line': [self.data['LFHCE'], self.data['LFH3']]},
                        #    {'line': [self.data['LOPAC'], left_femoral_head_extrusion_index_point]}]
                           {'line': [self.data['LOPAC'], self.data['LFH1']]}]

                },
                'right':{'value': right_femoral_head_extrusion_index, 'key_point': ['RFH1', 'RFHCE','LIPTE','RIPTE','ROPAC','RFH3'],
                    'drawer_point': [
                               {'line': [self.data['RFHCE'], self.data['RFH3']]},
                            #    {'line': [self.data['ROPAC'], right_femoral_head_extrusion_index_point]}]}}
                               {'line': [self.data['ROPAC'], self.data['RFH1']]}]}}

            # 'Radius': {
            #     'value': self.radius, 'key_point': ['RFH1', 'RFHCE', 'LFH1', 'LFHCE'],
            #     'drawer_point':[{'line': [self.data['RFH1'], self.data['RFHCE']]},
            #               {'line': [self.data['LFH1'], self.data['LFHCE']]}]},
        }
        return value_dict


def process_hip_data(csv_dir,json_dir):
    ## 处理髋关节的标记数据，转成json格式。
    Point_Name_Dict = {
        "{'关键点': ['LOPAC']}":0,
        "{'关键点': ['LIPSS']}":1,
        "{'关键点': ['ROPAC']}":2,
        "{'关键点': ['RIPTE']}":3,
        "{'关键点': ['LFHCE']}":4,
        "{'关键点': ['RFHCE']}":5,
        "{'关键点': ['RIPSS']}":6,
        "{'关键点': ['LIPTE']}":7,
        "{'关键点': ['RFH1']}":8,
        "{'关键点': ['RFH2']}":9,
        "{'关键点': ['RFH3']}":10,
        "{'关键点': ['LFH1']}":11,
        "{'关键点': ['LFH2']}":12,
        "{'关键点': ['LFH3']}":13,
    }
    point_dict = {}
    csv_data = pd.read_csv(csv_dir)
    csv_data_point = csv_data[(csv_data['comment'] == "['', '']")]
    ## 处理point
    last_name = csv_data_point.iloc[0].loc['sop']
    point_dict[csv_data_point.iloc[0].loc['sop']] = []
    for index in range(len(csv_data_point)):
        if last_name != csv_data_point.iloc[index].loc['sop']:
            point_dict[csv_data_point.iloc[index].loc['sop']] = []
        point = [int(csv_data_point.iloc[index].loc['points'].split(',')[0].replace('[[', '')),
                 int(csv_data_point.iloc[index].loc['points'].split(',')[1].replace(']]', '').replace(' ', ''))]
        point = point[::-1]
        point_dict[csv_data_point.iloc[index].loc['sop']].append(
            [point, Point_Name_Dict[csv_data_point.iloc[index].loc['selected']]])
        last_name = csv_data_point.iloc[index].loc['sop']

    json_list = []
    for k, value in point_dict.items():
        # if os.path.exists(os.path.join(jpg_path, k + '.jpg')):
        for i in value:
            if i[0][0] < 0 or i[0][1] < 0:
                value.remove(i)
        ## 针对存在缺失值的数据 进行处理.
        value_name = [p[1] for p in value]
        true_name = [i for i in range(14)]
        miss_name = [p for p in true_name if p not in value_name]
        for j in miss_name:
            value.append([[0, 0], j])
        json_list.append({
            'name': k,
            'point': sorted(value, key=lambda j: j[1])
        })
    print(len(json_list))
    with open(json_dir, 'w') as f:
        json.dump(json_list, f)

def get_error(pred_json,label_json,index= 0,distance = 1):
    pred = json.load(open(pred_json))
    label = json.load(open(label_json))
    error_list = []
    pred_dict = {}
    for pred_data in pred:
        pred_point = pred_data['point']
        pred_point = pred_point[4:11] + [pred_point[12]] + [pred_point[13]] + [pred_point[0]] + [pred_point[11]]
        pred_dict[pred_data['name']] = pred_point
    for label_data in label:
        label_point = label_data['point'][index][0]
        pred_point = pred_dict[label_data['name']][index]
        # print(label_point,pred_point)
        error_list.append(((label_point[0]-pred_point[0])**2 + (label_point[1]-pred_point[1])**2)**0.5 * 0.143)
    good_error = [p for p in error_list if p < distance]
    print(np.mean(error_list),len(good_error)/len(error_list))



if __name__ == '__main__':
    import json
    import pandas as pd
    DISTANCE = 5
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=0,distance=DISTANCE)
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=1,distance=DISTANCE)
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=2,distance=DISTANCE)
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=3,distance=DISTANCE)
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=4,distance=DISTANCE)
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=5,distance=DISTANCE)
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=6,distance=DISTANCE)
    get_error(pred_json='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json',label_json='test_0427.json',index=7,distance=DISTANCE)


    #
    # process_hip_data(csv_dir='D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/HIP_20210427.csv',
    #                  json_dir='test_0427.json')
    #
    #
    # pred_list = json.load(open('D:\wechat_file\WeChat Files\wxid_9944169443012\FileStorage\File/2021-04/pred_0401.json'))
    # label_list = json.load(open('test_0427.json'))
    # csv_data = pd.DataFrame(columns=['name','L_ce_angle','R_ce_angle','L_tonnis','R_tonnis','L_sharp','R_sharp','L_hhe','R_hhe'])
    # name_list = ['LOPAC','LIPSS','ROPAC','RIPTE','LFHCE','RFHCE','RIPSS','LIPTE','RFH1','RFH2','RFH3','LFH1','LFH2','LFH3']
    # for data in label_list:
    #     point_list = [p[0] for p in data['point']]
    #     print(point_list)
    #     # point_list = point_list[4:11] + [point_list[12]] + point_list[1:4] + [point_list[13]] + [point_list[0]] + [point_list[11]]
    #     if len(point_list) < 14:
    #         continue
    #     point_dict = dict(zip(name_list,point_list))
    #     print(point_dict)
    #     calculate = HipMeasurement(point_dict,spacing=0.143).get_result()
    #     print(calculate)
    #     csv_data = csv_data.append(pd.DataFrame({'name':data['name'],
    #                                   'L_ce_angle':calculate['CE_angle']['left']['value'],
    #                                   'R_ce_angle': calculate['CE_angle']['right']['value'],
    #                                   'L_tonnis': calculate['Tonnis_angle']['left']['value'],
    #                                   'R_tonnis': calculate['Tonnis_angle']['right']['value'],
    #                                   'L_sharp':calculate['Sharp_angle']['left']['value'],
    #                                   'R_sharp':calculate['Sharp_angle']['right']['value'],
    #                                   'L_hhe': calculate['HHE_index']['left']['value'],
    #                                   'R_hhe': calculate['HHE_index']['right']['value'],
    #                                   },index=[0]))
    # csv_data = csv_data[['name','L_ce_angle','R_ce_angle','L_tonnis','R_tonnis','L_sharp','R_sharp','L_hhe','R_hhe']]
    # csv_data.to_csv('./label_calculate.csv')
    #
    #
    # data_merge = pd.merge(pd.read_csv('label_calculate.csv'), pd.read_csv('pred_calculate.csv'), on='name')
    # data_merge.to_csv('merge_calculate_0427_gt.csv',index=False)
